import socketio from 'socket.io';
import http from 'http';

export const io = new socketio.Server();
type KeyValue = { [key: string]: string };
let guestNumber = 1;
let nickNames: KeyValue = {};
let namesUsed: string[] = [];
let currentRoom: KeyValue = {};

function listen(server: http.Server) {
    io.listen(server);
    io.sockets.on('connection', (socket: socketio.Socket) => {
        guestNumber = assingGuestName(socket, guestNumber, nickNames, namesUsed);
        joinRoom(socket, 'Aaron');

        handleMessageBroadcasting(socket);
        handleNameChangeAttempts(socket, nickNames, namesUsed);
        handleRoomJoining(socket);
        socket.on('rooms', () => {
            const data = [...io.sockets.adapter.rooms.entries()].reduce(
                (obj: { [key: string]: Array<string> }, [key, value]) => ((obj[key] = Array.from(value)), obj),
                {}
            );
            socket.emit('rooms', data);
        });

        handleClientDisconnection(socket);
    });
}

/**
 * 用户昵称分配
 * @param socket
 * @param guestNumber
 * @param nickNames
 * @param namesUsed
 * @returns 访客数量
 */
function assingGuestName(socket: socketio.Socket, guestNumber: number, nickNames: KeyValue, namesUsed: string[]): number {
    let name = `Guest${guestNumber}`;
    nickNames[socket.id] = name;
    socket.emit('nameResult', {
        success: true,
        name: name
    });
    namesUsed.push(name);
    return guestNumber + 1;
}

/**
 * 进入聊天室相关逻辑
 * @param socket
 * @param room
 */
function joinRoom(socket: socketio.Socket, room: string) {
    socket.join(room);
    currentRoom[socket.id] = room;
    socket.emit('joinResult', { room });
    socket.broadcast.emit('message', {
        text: `${nickNames[socket.id]} 加入了 ${room} 房间`
    });
    let userInRoom = io.sockets.adapter.rooms.get(room);
    let usersInRoomSummary = `当前在房间${room}中的用户有: `;
    if (userInRoom && Array.from(userInRoom).length > 1) {
        Array.from(userInRoom).forEach((user, index) => {
            let userSocketId = user;
            if (userSocketId !== socket.id) {
                if (index > 0) {
                    usersInRoomSummary += ', ';
                }
                usersInRoomSummary += nickNames[userSocketId];
            }
        });
    } else {
        usersInRoomSummary = '';
    }
    socket.emit('message', { text: usersInRoomSummary });
}

/**
 * 处理更名请求的逻辑
 * @param socket
 * @param nickNames
 * @param namesUsed
 */
function handleNameChangeAttempts(socket: socketio.Socket, nickNames: KeyValue, namesUsed: string[]) {
    socket.on('nameAttempt', (name: string) => {
        if (name.indexOf('Guest') === 0) {
            socket.emit('nameResult', {
                success: false,
                message: 'Names cannot begin with "Guest".'
            });
        } else {
            if (namesUsed.indexOf(name) === -1) {
                const previousName = nickNames[socket.id];
                const previousNameIndex = namesUsed.indexOf(previousName);
                namesUsed.push(name);
                nickNames[socket.id] = name;
                delete namesUsed[previousNameIndex];
                socket.emit('nameResult', {
                    success: true,
                    name: name
                });
                socket.broadcast.to(currentRoom[socket.id]).emit('message', {
                    text: `${previousName} is now known as ${name}.`
                });
            } else {
                socket.emit('nameResult', {
                    success: false,
                    message: 'That name is already in use.'
                });
            }
        }
    });
}
export type ClientMessage = { room: string; text: string };
/**
 * 转发client端发送过来的数据
 * @param socket
 */
function handleMessageBroadcasting(socket: socketio.Socket) {
    socket.on('message', (message: ClientMessage) => {
        socket.broadcast.emit('message', {
            text: `${nickNames[socket.id]}: ${message.text}`
        });
    });
}

export type ClientJoin = { newRoom: string };
/**
 * 创建房间
 * @param socket
 */
function handleRoomJoining(socket: socketio.Socket) {
    socket.on('join', (room: ClientJoin) => {
        socket.leave(currentRoom[socket.id]);
        joinRoom(socket, room.newRoom);
    });
}

/**
 * 用户断开连接
 * @param socket
 */
function handleClientDisconnection(socket: socketio.Socket) {
    socket.on('disconnect', () => {
        const nameIndex = namesUsed.indexOf(nickNames[socket.id]);
        delete namesUsed[nameIndex];
        delete nickNames[socket.id];
    });
}

export default listen;
